home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
HPAVC
/
HPAVC CD-ROM.iso
/
PXDTUT2.ZIP
/
PXDTUT2.TXT
< prev
Wrap
Text File
|
1997-04-17
|
23KB
|
581 lines
|====================================|
| |
| TELEMACHOS proudly presents : |
| |
| Part 2 of the PXD trainers - |
| |
| EMS-HANDLING |
| the way to do it <g> |
| |
|====================================|
___---__--> The Peroxide Programming Tips <--__---___
<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
Intoduction
-----------
Hiya... I'm Telemachos of Peroxide - a new danish group (yes... we DO have
groups in Denmark too :))) )
In my last trainer "DOOM-walls : The technique and tricks" (which is really
great, and I think you should leech it RIGHT away from the net :) ) I promised
to do a trainer on the subject : Using the PC's memmory above the 640K limit
which is set when using the standard memory-handling procedures provided with
Turbo Pascal.
Throughout this trainer I'll drop code in Pascal - with quite a load of asm
code build in.
But (as mentioned earlier.. uhmm.. like on line above this one) as most of
the code is asm it should be easy to port to other languages like C/C++ or
TASM (no kiddin' ?? :) ).
So, this trainer won't teach you to do colorful graphic effects that'll
impress your friends and make your girlfriend proud of you.
It will perhaps also be a bit boring when I start talking about the PC's
hardware-limitations and stuff...
But I promise you - it'll be well worth the reading time anyway as it'll give
you the key to fast computer graphic - the key to storing ALL your data in
memory before the program executes :)
So here we go.... "EMS-HANDLING : the way to do it :) "
*************************** ATTENTION ARTISTS!!! *****************************
ARE YOU AN ARTIST ?
DO YOU DRAW VGA-BITMAPS ?
CAN YOU DO GFX IN ALL RESOLUTIONS - 320x200x256 AND VARIOUS SVGA-MODES ?
DO YOU WANT TO SEE YOUR WORK IN AMAZING PRODUCTIONS ?
CAN YOU DO GAME-GFX AS WELL AS BACKGROUNDS FOR DEMOS ?
IF YOU MEET THE ABOVE TERMS (or some at least :) ), THEN DROP ME (TELEMACHOS)
A MESSAGE OR MAIL THE GROUP AT : Peroxide@image.dk
******************************************************************************
If you want to get in contact with me, there are several ways of doing it :
1) Write me via FIDO-net : 2:235/350.22
2) E-mail me : tm@image.dk
3) Snail mail me : Kasper Fauerby
Saloparken 226
8300 Odder
Denmark
4) Call me (Voice ! ) : +45 86 54 07 60
Get this serie from the major demo-related FTP-sites or from our own homepage
(soon at least ;) ) : http://www.image.dk/~peroxide
or directly from my own : http://www.image.dk/~tm
Extended vs. expanded memory
----------------------------
We all know that the memory above 1MB can be either Extended (XMS) or Expanded
(EMS) memory - but why do we need theese two "different" kind of memory types ?
Why can't it all just be MEM ???
The awnser lies all the way back when a guy called Bill Gates sat at the
keyboard typing away on his main project - DOS!
At that time 1MB of mem was a HUGE amount of mem.. so the guy never gave it
a thought that perhaps someday even 1MB of mem would'nt be enough.
He never thought that someday HE himself would release a OS that required
16MB of mem - just to run!!
So.. DOS was made with a limitation of 1MB of mem. The first 640Kb being
conventional memory - the rest being himem.
But as time went on and people started to make up more and more complicated
programs, using more and more mem, it soon became aparent that if someone
did'nt came up with a way of supporting more memory, then DOS would die.
So... eventually some clever clever guys came up with the XMS and EMS
specifications - each with it's own advantages and disadvantages.
The main reason why I have chosen to descripe the use of EMS memory is, that
EMS-memory by far is the easiest himem to use.
Its disadvantages (which I will descripe later) lies in the page-size, but
with some clever codin' theese problems can be solved.
What exactly is SEGMENTS and OFFSETS ?
---------------------------------------
DOS in real-mode is a 16bit environment. This means that DOS uses the old
16bit registers originating from the good ol' 8086/8088 processor.
Well, to be able to address every single byte of memory we have in the computer
each byte has to have a unique address.
If we use only one register (16bit) for such an address some quick calculations
reveals that the largest address available would be :
Largest address = 2^16 = 65536
Not very much ehh ?? Ie. using this model we can only address 64K of mem!!!!
Even Bill Gates saw a problem in this and so he decided to use a few more bits
to address the memory.
Using 17 bits gives us a limit of 128K, 18 bits 256K, 19 bits 512Kb and finally
the amount that Bill decided to use - namely 20 bits wich gives us a total of
1024 (1MB) of addressable memory.
But how do you address a 20bit memory location using only 16bit registers ?
Well.. the awnser lies in dividing the memory into MEMORY SEGMENTS.
To address a specific address in such a segment a 16bit OFFSET is used to
determine how far IN in the SEGMENT the information is placed. This makes each
segment 64K big.
Each SEGMENT also have a 16bit base address (fx. $a000 for the video card).
When accessed the SEGMENT address is shifted 4 bits to the left - making it a
20bit value. The 16bit OFFSET address is now added to this 20bit address - the
result being the final memory address.
In theory a SEGMENT could begin at any given memory location, but as it's
shifted 4 places to the left (multiplied by 16) all SEGMENT will begin at an
address divisible by 16.
To sum things up we can have 2^16 = 65536 different segments within the 1MB
boundry set by the 20bit system.
Lets take a look at the video card placed at $a000. This being a 16bit value
we shift it 4 places to the left by multiplying it with 16. This gives us the
memory location where the video card begin. $a000 shl 4 = $a0000 (hell... hex
IS pretty neat huh ?? ) = 655360 bytes = 640K.
WOW.. the card is JUST after the conventional memory :) So now you know what
the memory from 640K to 704K does :)
So every single byte of memory (below 1MB that is) is addressed through its
SEGMENT and an OFFSET in this segment.
As an example lets take a look at the VGA screen again. Its SEGMENT being
$a000 all we need to know is the offset. In mode 13h the memory layout is
lineary - ie. the first pixel being at offset 0, the second at offset 1 and so
on. Lets say we want to plot the 256th pixel on screen (coordinates ( 256,0)).
Then the offset will be 256 = $100 - so the address desciped in 16bit registers
is
pixel : $a000:$0100
Yeah right... I knew all that - but what about EMS?
---------------------------------------------------
EMS is - as mentioned earlier - just a name for the physical memory above the
1MB boundry.
Actually this mem is only *made* EMS mem because of the LIM-specifications.
The LIM expanded memory specifications is a software interface between the
expanded memory manager (EMM) and the software that wishes to use the EMS
memory. LIM supports up to 32M of EMS memory.
Because the computer (still talkin' real mode) can only physical address memory
below 1M the EMS memory is handled through a WINDOW placed in himem - that is
between 640K and 1024K.
This window is called the page-frame. In the page-frame is several PHYSICAL EMS
pages - each with the size of 16K - which I'll return to later.
The EMS memory is divided into a number of LOGICAL pages... also with a size of
16K.
The trick is to map these LOGICAL pages into the PHYSICAL pages... and then
address them as normal memory.
You can look at the EMS memory as a giant piece of paper with information
written on it - but what you actually see of this information is determined
by the position of the "windows" in the page frame.
Take a look at this diagram :
----------------------------- up to 32MB
| |
| EMS memory : |
| |
| divided into lots of |
| 16K blocks... LOGICAL |
| pages! |
| |
| |
|=============================| 1024K (1MB)
/ | / / / / / / / / / / / / / / |
/ |-----------------------------| 960K
THIS IS / | THE PAGE FRAME : |
HIMEM! / | |
\ | divided into 12 16K |
\ | physical memory blocks |
\ |-----------------------------| 768K
\ | / / / / / / / / / / / / / / |
|=============================| 640K
/ | |
/ | 24 16K physical pages |
/ | intended only for |
/ | operating system / |
THIS IS / | environment |
CONVEN- \ |-----------------------------| 256K
TIONAL \ | / / / / / / / / / / / / / / |
MEMORY \ | / / / / / / / / / / / / / / |
\ | / / / / / / / / / / / / / / |
\ |=============================| 0K
Besides the page frame, himem is stuffed with things like the video memory and
all sorts of devices and drivers you load with the LoadHigh and DeviceHigh
commands in your autoexec.bat and config.sys.
Now, this is why you have so much less himem to play with when running EMS -
the page frame swallows quite a bit of memory :)
Ok... Now I understand EMS - How do I code it ???
--------------------------------------------------
Well.. the EMS functions are all controlled through int 67h.
There are LOTS of functions, but I will only descripe the most important here.
For a complete reference to all of the functions in int 67h get a copy of the
file ems4spec.doc which is a complete transcription of the LIM-specifications.
It is not included here with this tuturial as it's a 450Kb text file :)
I think the easiest way to understand the different basic EMS functions is to
go through an EMS unit I have created for this tuturial. Each procedure /
function will be explained as we go through them.
[ cut here for a rip of the unit TMEMS.PAS]
Unit TMEMS;
INTERFACE
Var Handle : integer;
{by declaring this a public variable I assume that you will only be allocating
EMS pages ONCE during a program.
It saves you from having to pass the handle to a couple of functions every
time you call them.
But then you can't fx. allocate 10 pages at the beginning of the program -
and then allocate 15 more later.
If you wan't to do that you'll have to rewrite a couple of the routines so
that the correct handle must be passed to them when called.}
Function Hex_String (Number: Integer): String;
Function EMS_AreYouThere : Boolean;
Procedure Pagestatus(Var Total, Available: Integer);
Procedure Allocate_Pages(Needed: Integer);
Procedure Map_Page(Logical : Integer;Physical : byte);
Function Get_Frame_Address : Integer;
Procedure Deallocate_Pages;
Function Get_Version_Number : string;
IMPLEMENTATION
Function Hex_String (Number: Integer): string;
Function Hex_Char (Number: Integer): Char;
Begin
If Number < 10 then Hex_Char := Char (Number + 48)
else Hex_Char := Char (Number + 55);
end; { Function Hex_char }
Var
S: string;
Begin
S := '';
S := Hex_Char ((Number shr 1) div 2048);
Number := (((Number shr 1) mod 2048) shl 1) + (Number and 1);
S := S + Hex_Char (Number div 256);
Number := Number mod 256;
S := S + Hex_Char (Number div 16);
Number := Number mod 16;
S := S + Hex_Char (Number);
Hex_String := S + 'h';
end; { Function Hex_String }
{uhm... this procedure is ripped from the file ems4spec.doc }
{***********************}
Function EMS_AreYouThere : Boolean;
Var
EMS_Name : string[8];
Returned_Name : string[8];
Position : integer;
segm : word;
Begin
Returned_Name := '';
EMS_Name := 'EMMXXXX0'; {this is the ID string that SHOULD appear
in the code segment of int 67h}
asm
mov ah,35h
mov al,67h
int 21h
mov segm,es
end;
For Position := 0 to 7 do
Returned_Name := Returned_Name + Chr (mem[segm:Position + $0A]);
{This call will return the segment address where the ID string SHOULD
be.
If the ID string is there it'll be placed from offset $0A to $11}
If Returned_Name = EMS_Name
then EMS_AreYouThere := true
else EMS_AreYouThere := false;
end; { Function EMS_AreYouThere }
{*******************}
Procedure Pagestatus(Var Total, Available: Integer);
Var
HowManyInAll : word;
HowManyAvailable : word;
Begin
asm
mov ah,42h {this is EMS function nr. 42h }
int 67h
mov HowManyAvailable,bx
mov HowManyInAll,dx
end;
Available:=HowManyAvailable;
Total:=HowManyInAll;
end; { Function Pagestatus }
{This Procedure is nice when you want to know if there is enough free EMS
memory to run your program.}
{*************}
Procedure Allocate_Pages(Needed: Integer);
Assembler;
asm
mov ah,43h {this is EMS function nr. 43h}
mov bx,[Needed]
int 67h
mov [handle],dx {NOT very nice... but heck... I like it this way}
end; { Function Allocate_Pages }
{When you run this procedure you allocate a certain amount of EMS pages.
A handle is then assigned to these pages. Those of you who code TASM know of
handles from file handling routines.
But those of you who just use Pascal are'nt used to having a number assigned
to a file or a piece of memory. Well.. its basicly the same thing as when you
use the Assign comand in TP. Here you assign a string - namely the filename -
to a var of the type : file (or file of bla bla bla). Later you use this var
when you want to manipulate with the file.
Same thing here - don't think about it.}
{*****************}
procedure Map_Page(Logical : Integer;Physical : byte);
Assembler;
asm
mov ah,44h {EMS function nr. 44h}
mov dx,[handle] {humm... NO comments :) }
mov bx,[logical]
mov al,[Physical]
int 67h
end; { Function Map_Page }
{This procedure sets a window in the page frame to a certain logical page
in EMS memory.
From now on, when you manipulate with the physical page in the page frame
you manipulate with the mapped logical page in EMS}
{****************}
Function Get_Frame_Address : Integer;
Assembler;
asm
mov ah,41h {EMS function nr. 41h}
int 67h
mov ax,bx
end;
{This function returns the segment address of the page frame.
Now this one is VERY!! important. When mapping logical pages into physical
pages the programmer needs to know where to address the physical pages.
Each physical page in the page frame has its own segement address.
Physical page nr. 0 has THE SAME ADDRESS AS THE PAGE FRAME.
From then on the physical pages are $400 (1Kb) apart - ie :
(say that the page frame address is $D000 - it often is )
physical page nr. segment address
0 $D000
1 $D400
2 $D800
3 $DC00
4 $E000
}
{*****************}
Procedure Deallocate_Pages;
Assembler;
asm
mov ah,45h
mov dx,[handle] {humm... keeps popping up everywhere }
int 67h
end; { Procedure Deallocate_Pages }
{This returns the allocated EMS pages to the memory pool.
If you don't call this when closing a program down the EMS
memory will be useless to all other programs too }
{************}
Function Get_Version_Number : String;
Var
Integer_Part, Fractional_Part: byte;
Begin
asm
mov ah,46h
int 67h
cmp ah,0
jne @error
mov bl,al
shr bl,4
add bl,48
mov Integer_part,bl
mov bl,al
and bl,$F
add bl,48
mov Fractional_Part,bl
@error :
end;
Get_version_number:=chr(Integer_part)+'.'+chr(Fractional_part);
end; { Function Get_Version_Number }
{Ripped from ems4spec.doc - well.. the idea that is, rewritten to asm by ME <G>}
[ end unit code ]
Writing a program that uses EMS memory
---------------------------------------
OK.. when writing a program that uses EMS memory there are certain things that
you must ALWAYS do.
1) Initialize the EMM manager. Call EMS_AreYouThere to see if EMS is available.
2) Check if all the pages needed are available in the current system. You'll
have to try and calculate how many EMS pages your program is going to use.
3) Allocate these pages and (if you don't use my unit) store the EMS handle in
some logically named variable.. like : "EMS_handle" :)
4) Get the address of the PAGE FRAME. Store this address in a variable - I
personally likes to use the name FADDR (frame address).
When ever you want to write to EMS memory you do the following :
Mem[Faddr+Phys_PageNr * $400 : offset_in_page] := value_to_be_stored;
Voila! Now you have written to the LOGICAL EMS page that was mapped in
Phys_PageNr.
Often You'll find that you only have to use Physical Page nr 0...
If this is the case in your program simply use
Mem[Faddr: offset_in_page] := Value_to_be_stored;
In my Eye Of The Beholder demo from tuturial 1 I only use ONE physical page.
5) Go ahead and use the memory. When writing to EMS map the correct page into
a physical page and write to this one - when reading from an EMS page map
the page to the physical page and read from this one.
It really IS as simple as this!
6) When you are through using the EMS memory don't forget to deallocate the
used pages. Otherwise they will be unuseable by the system in any other
applications.
Advantages / Disadvantages
---------------------------
The main advantage of using EMS memory is the ease of coding for it :)
Really - with a few routines as those listed earlier in this tuturial you can
code applications using up to 32MB of memory as easy as if it were all
conventional memory. Play with the routines for an hour or so.... and you'll
master EMS.
Also EMS is fairly quick. Only an interrupt is required for mapping a LOGICAL
EMS page into addressable memory. And with multiple physical pages available
and some clever coding / structuring there should'nt be to many swaps in the
program.
Those of you who want to develop futher routines for handling EMS routines
should take a look at the file ems4spec.doc - many more complicated functions
are available through int 67h. Fx. I could mention that it IS possible to map
multiple pages at the same time....
The main disadvantage of EMS memory is the page size. It really SUCK that an
EMS pages originally was defined as 16K of memory. Why not 64K so that we
could fit an entire segment into one page.
As graphical programmers one of the first uses for EMS memory that we think of
is storing graphic (well... I did anyway). Here a page size of 64K would be
really cool as long as we stay in mode 13h. In that case we could store an
entire screen in one EMS page...
Well... unfortunately EMS pages is only 16K so we'll have to think of some
other uses. One could split the screen up into 4 16K pieces... and then use
4 EMS pages pr. screen. But this is a little awkward as numerous interrupt
calls must be made to map all four EMS pages needed to store the image.
And when the image is to be used numerous physical_pages must be used - or the
moving of data to the videocard must be split down into 4 parts.... one for
each EMS page used.
But fortunately lots of other datastructures fit into a 16K block of memory.
Textures fx. is typically 64X64 or 128X128 pixels in size - making them take
up 4K or 16K of memory.
In my 3d-world each level supports up to 100 different textures - each of the
size 128X128. It would be far to slow to load them from disk when they were to
be used in runtime. But 100 textures of 16K of mem each... they take up 1.6MB
of memory!
Also each level supports up to 20 different monsters using 5 frames (128X128)
each to animate movement - that's 100 16K blocks more!
So all in all my game takes up 3.2MB of memory - I could'nt have done it
without EMS memory.
So - sprites in general, smaller bitmaps that does'nt take up the entire
screen, lenses in demos, phongmaps, data structures for 3d objects - you name
it.
All those smaller structures often fit into an EMS page. When coding for EMS
it is often required that you think about your structures when defining them.
Perhaps you could split up some of your BIG records into several smaller ones ?
Last remarks
-------------
Well, that's about all for now.
Hope you found this doc useful - and BTW : If you DO make anything public using
these techniques please mention me in your greets or where ever you se fit.
I DO love to see my name in a greeting :=)
What to do next??? Humm... I think I'll do something a little more colorfull
next time - otherwise I'll just scare of my readers :)
One guy requested bitmap rotation - another would like some more 3d-stuff...
But one of these days I'm planning on doing a doc on interrupts - and how to
use them. Comments ??
If you have any good ideas for a subject you wish to see a tuturial on please
mail me. If I like the idea (and know anything about it :) ) I'll write a
tut on it.
Humm... yeah - while I remember it.
These docs will soon be available through my very own homepage (linked to our
group homepage) so check out the following addresses :
Peroxide Homepage : http://www.image.dk/~peroxide (this one is up by now)
Telemachos' Page : http://www.image.dk/~tm (SHOULD be up when you
read this :) )
Keep on coding and CuL8'er M8's
Telemachos - April '97.